#include <iostream>
#include <sstream>

#include "citizen.hpp"
#include "foreach.hpp"
#include "government.hpp"
#include "money_market.hpp"

namespace {

bool OfferPriceLess(const money_market::offer& a, const money_market::offer& b)
{
	return a.price == b.price ? a.quantity < b.quantity : a.price < b.price;
}

bool OfferPriceMore(const money_market::offer& a, const money_market::offer& b)
{
	return a.price == b.price ? a.quantity > b.quantity : a.price > b.price;
}

}

money_market::money_market(government_ptr gov) : tax_rate_(10), owner_(gov)
{
	turn_reference_counting_off();
}

bool money_market::can_buy(const_item_type_ptr i) const
{
	return cannot_buy_.count(i) == 0;
}

bool money_market::can_sell(const_item_type_ptr i) const
{
	return cannot_sell_.count(i) == 0;
}

bool money_market::prepare_buys_sells(std::vector<money_market::offer>& buys,
                                      std::vector<money_market::offer>& sells,
									  int& quantity, int& price, int& tax)
{
	std::sort(buys.begin(), buys.end(), OfferPriceMore);
	std::sort(sells.begin(), sells.end(), OfferPriceLess);

	if(buys.empty() || sells.empty()) {
		return false;
	}

	int buy_quantity = 0, sell_quantity = 0;
	int buy_price = 0, sell_price = 0;

	std::vector<money_market::offer>::iterator b = buys.begin();
	std::vector<money_market::offer>::iterator s = sells.begin();
	while(b != buys.end() && s != sells.end() && b->price >= s->price + tax_on_sale(s->price)) {
		buy_price = b->price;
		sell_price = s->price;
		if(buy_quantity == sell_quantity) {
			buy_quantity += b->quantity;
			sell_quantity += s->quantity;
			++s;
			++b;
		} else if(buy_quantity < sell_quantity) {
			buy_quantity += b->quantity;
			++b;
		} else {
			sell_quantity += s->quantity;
			++s;
		}
	}

	while(b != buys.end() && buy_quantity < sell_quantity && b->price >= sell_price + tax_on_sale(sell_price)) {
		buy_price = b->price;
		buy_quantity += b->quantity;
		++b;
	}

	while(s != sells.end() && sell_quantity < buy_quantity && buy_price >= s->price + tax_on_sale(s->price)) {
		sell_price += s->price;
		sell_quantity += s->quantity;
		++s;
	} 

	price = buy_price;
	if(buy_price != sell_price + tax_on_sale(s->price)) {
		buy_quantity = 0;
		sell_quantity = 0;
		foreach(const money_market::offer& o, buys) {
			if(o.price >= buy_price) {
				buy_quantity += o.quantity;
			}
		}

		foreach(const money_market::offer& o, sells) {
			if(o.price <= sell_price) {
				sell_quantity += o.quantity;
			}
		}

		if(buy_quantity < sell_quantity) {
			std::cerr << "buyers dictate: " << buy_quantity << " < " << sell_quantity << ": " << sell_price << " not " << buy_price << "\n";
			//supply outstrips demand, so the price is dictated by buyers
			price = sell_price;
		} else if(buy_quantity > sell_quantity) {
			std::cerr << "sellers dictate: " << buy_quantity << " > " << sell_quantity << ": " << buy_price << " not " << sell_price << "\n";
			//demand outstrips supply, so the price is dictated by sellers
			price = buy_price;
		} else {
			price = (buy_price + sell_price)/2;
		}

		while(price + tax_on_sale(price) > buy_price) {
			--price;
		}
	}

	tax = tax_on_sale(price);

	if(b == buys.begin() || s == sells.begin()) {
		sell_prices_[sells.front().type->id()] = sells.front().price;
		buy_prices_[sells.front().type->id()] = buys.front().price;
		return false;
	}


	buy_prices_[sells.front().type->id()] = price;
	sell_prices_[sells.front().type->id()] = price;
	quantity = std::min(buy_quantity, sell_quantity);

	trade_quantities_[sells.front().type->id()] = quantity;
	trade_taxes_[sells.front().type->id()] = tax*quantity;
	return true;
}

void money_market::resolve_buys_sells(std::vector<money_market::offer>& buys,
                                      std::vector<money_market::offer>& sells)
{
	int price, tax;
	int buy_quantity, sell_quantity;
	if(prepare_buys_sells(buys, sells, buy_quantity, price, tax) == false) {
		return;
	}

	sell_quantity = buy_quantity;

	static const std::string MoneyItem = "silver";

	std::cerr << "mm trade " << buy_quantity << " items\n";
	foreach(money_market::offer& b, buys) {
		if(!buy_quantity || b.price < price) {
			continue;
		}
		const int afford_quantity = b.trader->items_owned(MoneyItem)/(price + tax);
		int quantity = std::min(std::min(buy_quantity, b.quantity), afford_quantity);

		buy_quantity -= quantity;
		b.trader->get_item(b.type->id(), quantity);
		b.trader->get_item(MoneyItem, -quantity*(price + tax));
		if(owner_) {
			owner_->get_money(quantity*tax);
		}

		b.quantity -= quantity;
	}

	//if there's any buyers who didn't have enough money so the buy quantity
	//couldn't be fulfilled, subtract from the sell quantity
	sell_quantity -= buy_quantity;

	trade_quantities_[sells.front().type->id()] = sell_quantity;
	trade_taxes_[sells.front().type->id()] = tax*sell_quantity;

	foreach(money_market::offer& s, sells) {
		if(!sell_quantity) {
			continue;
		}
		const int quantity = std::min(sell_quantity, s.quantity);
		sell_quantity -= quantity;
		s.trader->get_item(s.actual_item.empty() ? s.type->id() : s.actual_item, -quantity);
		s.trader->get_item(MoneyItem, quantity*price);
		s.quantity -= quantity;
	}
}

int money_market::tax_on_sale(int amount) const
{
	return (amount*tax_rate_)/100 + ((amount*tax_rate_)%100 ? 1 : 0);
}

void money_market::prepare_market()
{
	trade_quantities_.clear();
	trade_taxes_.clear();
	sell_prices_.clear();
	buy_prices_.clear();
	int quantity, price, tax;
	for(std::map<const_item_type_ptr, std::vector<offer> >::iterator i = buys_.begin(); i != buys_.end(); ++i) {
		prepare_buys_sells(i->second, sells_[i->first], quantity, price, tax);
	}
}

void money_market::resolve_market()
{
	cannot_buy_.clear();
	cannot_sell_.clear();
	trade_quantities_.clear();
	trade_taxes_.clear();

	//first resolve all markets with downgrades. Then move their left over
	//sell orders to the downgrade market.
	for(offer_map::iterator i = sells_.begin(); i != sells_.end(); ++i) {
		if(i->first->downgrades_to().empty()) {
			continue;
		}

		resolve_buys_sells(i->second, sells_[i->first]);
		
		//Now put all of our sells in the downgrade market.
		std::vector<offer>& downgrade_sells = sells_[item_type::get(i->first->downgrades_to())];
		foreach(const offer& o, i->second) {
			if(o.quantity > 0) {
				downgrade_sells.push_back(o);
			}
		}
	}

	//now process all the non-downgradable markets.
	for(offer_map::iterator i = buys_.begin(); i != buys_.end(); ++i) {
		if(i->first->downgrades_to().empty() == false) {
			continue;
		}

		resolve_buys_sells(i->second, sells_[i->first]);
	}
}

void money_market::add_buy(const offer& o)
{
	std::cerr << "mm add buy\n";
	buys_[o.type].push_back(o);
}

void money_market::add_sell(const offer& o)
{
	std::cerr << "mm add sell\n";
	sells_[o.type].push_back(o);
	assert(o.trader->items_owned(o.type->id()) >= o.quantity);
}

void money_market::clear()
{
	buys_.clear();
	sells_.clear();
}

std::string money_market::description() const
{
	std::ostringstream s;
	for(std::map<std::string, int>::const_iterator i = trade_quantities_.begin(); i != trade_quantities_.end(); ++i) {
		s << "Trade " << i->second << " " << i->first << " at " << last_sell_price(i->first) << "\n";
	}

	s << "\n";

	for(std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = buys_.begin(); i != buys_.end(); ++i) {
		std::map<int, int> prices;
		for(std::vector<offer>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
			prices[j->price] += j->quantity;
		}

		for(std::map<int, int>::const_iterator j = prices.begin(); j != prices.end(); ++j) {
			s << "  Want to buy " << j->second << " " << i->second.front().type->id() << " for " << j->first << "\n";
		}
	}

	for(std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = sells_.begin(); i != sells_.end(); ++i) {
		std::map<int, int> prices;
		for(std::vector<offer>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
			prices[j->price] += j->quantity;
		}

		for(std::map<int, int>::const_iterator j = prices.begin(); j != prices.end(); ++j) {
			s << "  Want to sell " << j->second << " " << i->second.front().type->id() << " for " << j->first << "\n";
		}
	}

	return s.str();
}

int money_market::last_buy_price(const std::string& item) const
{
	std::map<std::string, int>::const_iterator i = buy_prices_.find(item);
	if(i != buy_prices_.end()) {
		return i->second;
	}

	return 2;
}

int money_market::last_sell_price(const std::string& item) const
{
	std::map<std::string, int>::const_iterator i = sell_prices_.find(item);
	if(i != sell_prices_.end()) {
		return i->second;
	}

	return 10;
}

int money_market::can_buy_at(const const_item_type_ptr& item) const
{
	std::cerr << "BUY " << item->id() << " ";
	//this is the price that the player can buy goods at. If trades are going
	//on, we use the market price. If no trades are going on, then we can go
	//for the cheapest seller.
	std::map<std::string, int>::const_iterator i = sell_prices_.find(item->id());
	if(i != sell_prices_.end()) {
		std::cerr << "at market " << i->second << "\n";
		return i->second;
	}
	
	std::map<const_item_type_ptr, std::vector<offer> >::const_iterator j = sells_.find(item);
	if(j != sells_.end() && j->second.empty() == false) {
		std::cerr << "best offer " << j->second.front().price << "\n";
		return j->second.front().price;
	}
	std::cerr << "none\n";

	return -1;
}

int money_market::can_sell_at(const const_item_type_ptr& item) const
{
	std::cerr << "SELL " << item->id() << " ";
	//this is the price that the player can sell goods at. If trades are going
	//on, we use the market price. If no trades are going on, then we can go
	//for the highest bidder.
	std::map<std::string, int>::const_iterator i = buy_prices_.find(item->id());
	if(i != buy_prices_.end()) {
		std::cerr << " at market " << i->second << "\n";
		return i->second;
	}
	
	std::map<const_item_type_ptr, std::vector<offer> >::const_iterator j = buys_.find(item);
	if(j != buys_.end() && j->second.empty() == false) {
		std::cerr << "Best offer " << j->second.front().price << "\n";
		return j->second.front().price;
	}

	std::cerr << "none\n";
	return -1;
}

int money_market::last_quantity(const std::string& item) const
{
	std::map<std::string, int>::const_iterator i = trade_quantities_.find(item);
	if(i != trade_quantities_.end()) {
		return i->second;
	}

	return 0;
}

int money_market::last_taxes(const std::string& item) const
{
	std::map<std::string, int>::const_iterator i = trade_taxes_.find(item);
	if(i != trade_taxes_.end()) {
		return i->second;
	}

	return 0;
}

int money_market::last_tax_revenue() const
{
	int revenue = 0;
	for(std::map<std::string, int>::const_iterator i = trade_taxes_.begin(); i != trade_taxes_.end(); ++i) {
		revenue += i->second;
	}

	return revenue;
}

void money_market::goods_traded(std::set<const_item_type_ptr>& goods) const
{
	for(std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = buys_.begin(); i != buys_.end(); ++i) {
		goods.insert(i->first);
	}

	for(std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = sells_.begin(); i != sells_.end(); ++i) {
		goods.insert(i->first);
	}
}

namespace {
std::vector<money_market::offer> empty_offers;
}

const std::vector<money_market::offer>& money_market::buy_offers(const_item_type_ptr item) const
{
	std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = buys_.find(item);
	if(i != buys_.end()) {
		return i->second;
	}

	return empty_offers;
}

const std::vector<money_market::offer>& money_market::sell_offers(const_item_type_ptr item) const
{
	std::map<const_item_type_ptr, std::vector<offer> >::const_iterator i = sells_.find(item);
	if(i != sells_.end()) {
		return i->second;
	}

	return empty_offers;
}

bool money_market::buy_item(const_item_type_ptr item, int price, bool execute)
{
	std::vector<offer>& offers = sells_[item];
	int index = 0;
	foreach(offer& o, offers) {
		if(o.price <= price && o.trader->items_owned(item->id())) {
			if(execute) {
				cannot_sell_.insert(item);
				o.trader->get_item(item->id(), -1);
				o.trader->get_item("silver", price);
				o.quantity--;
				if(!o.quantity) {
					offers.erase(offers.begin() + index);
				}
			}
			return true;
		}

		++index;
	}

	return false;
}

bool money_market::sell_item(const_item_type_ptr item, int price, bool execute)
{
	std::vector<offer>& offers = buys_[item];
	int index = 0;
	foreach(offer& o, offers) {
		if(o.price >= price && o.trader->items_owned("silver") >= price) {
			if(execute) {
				cannot_buy_.insert(item);
				o.trader->get_item(item->id(), 1);
				o.trader->get_item("silver", -price);
				o.quantity--;
				if(!o.quantity) {
					offers.erase(offers.begin() + index);
				}
			}
			return true;
		}

		++index;
	}

	return false;
}

variant money_market::get_value(const std::string& key) const
{
	static const std::string BuyPrefix = "buy_";
	static const std::string SellPrefix = "sell_";

	if(key.size() > BuyPrefix.size() && std::equal(BuyPrefix.begin(), BuyPrefix.end(), key.begin())) {
		std::string item(key.begin() + BuyPrefix.size(), key.end());
		const std::map<std::string, int>::const_iterator itor = buy_prices_.find(item);
		if(itor == buy_prices_.end()) {
			std::cerr << "could not find buy price for " << item << " returning 10\n";
			return variant(2);
		}

		std::cerr << "buy price for " << item << " = " << itor->second << "\n";

		return variant(itor->second);
	}

	if(key.size() > SellPrefix.size() && std::equal(SellPrefix.begin(), SellPrefix.end(), key.begin())) {
		std::string item(key.begin() + SellPrefix.size(), key.end());
		const std::map<std::string, int>::const_iterator itor = sell_prices_.find(item);
		if(itor == sell_prices_.end()) {
			return variant(100);
		}

		return variant(itor->second);
	}

	std::cerr << "queried mm with illegal key: '" << key << "'\n";

	return variant();
}
